/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief  Recursive-Descent-Function of non-terminal assignmentExpression.
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#include "../_parser_inner.h"

#define THIS_NT_ID  assignmentExpression

//
// This program provides a rough demonstration of
//    "how to implement error recovery"
//

static bool  is_assignOptr_(EnumTokenKind kind) {
    switch ( kind ) {
        case TK_OP_ASSIGN:      // ID  = ...
        case TK_OP_PLUSASSIGN:  // ID += ...
        case TK_OP_MINUSASSIGN: // ID -= ...
        case TK_OP_MULASSIGN:   // ID *= ...
        case TK_OP_DIVASSIGN:   // ID /= ...
        case TK_OP_MODASSIGN:   // ID %= ...
            return true;
        default:
            return false;
    }
}

// the set: FIRST(assignmentExpr)
static EnumTokenKind firstSet_assignmentExpr [] = {
        TK_ID,
        TK_INT_LITERAL,
        TK_REAL_LITERAL,
        TK_OP_PLUS,
        TK_OP_MINUS,
        TK_OP_NOT,
        TK_LPAREN,

        TK_ERROR      // JUST a BOUNDARY
};

// the set: FOLLOW(assignmentExpr)
static EnumTokenKind followSet_assignmentExpr [] = {
        TK_SEMI,
        TK_COMMA,
        TK_RPAREN,
        TK_RBRACKET,

        TK_ERROR      // JUST a BOUNDARY
};

// This function determines whether the next input token, terminal, is valid.
// In other words, Does the next token belong to the FIRST(assignmentExpr).
static bool nextTokenIsValid__(t_parser_ptr pThis) {
    EnumTokenKind  nextInput = LOOK_AHEAD(pThis);
    for(int i=0; TK_ERROR != firstSet_assignmentExpr[i];  ++i)
    {
        if (nextInput == firstSet_assignmentExpr[i])
            return true;
    }

    return false;
}



// assignmentExpression        //------------------ LL(*) production 73..74
//    :  leftValue
//       ('='  | '+=' | '-=' | '*=' | '/=' | '%=')
//       assignmentExpression
//    |
//       // if LA(1..K) is leftValue,  LA(K+1) is not assignOptr;
//       // if LA(1) is ID and LA(2) is '(',  this is a func-call.
//       logicalOrExpression
//
t_tree_ptr RDF_assignmentExpression (t_parser_ptr pThis) {
    int productionId = PRD_ID_UNKNOWN;
    t_tree_ptr root = tree_new_parent(THIS_NT, productionId);
    enter_nonterminal ( pThis, THIS_NT, root );
    CHECK_NULLTREE_ABORT ( root, root );
// -------  BEGIN of local action

    // This is the only one example of syntax error recovery in AMCC.
    // We present two strategies here:
    //   [[ Does your textbook mention these ideas? ]]
    //
    // (1) Perform Error-Prediction.
    //     This approach can intercept errors as early as possible,
    //     and avoid unnecessary recursive calls to RDF functions.
    //
    // (2) Perform Panic-Mode-Recovery when an error has detected.
    //     The parser discards one or more input tokens until one of
    //     designated set of "synchronizing tokens" is found.
    //     BUT, determining the "synchronizing tokens" is a complicated job.
    //

    if ( false == nextTokenIsValid__(pThis) ) {
        parser_report_badtoken(pThis, NEXT_TOKEN(pThis),
                               TK_ERROR,
                               "assignmentExpression");
        int syncWhatCase = parser_discard_tokens(pThis,
                              firstSet_assignmentExpr,
                              followSet_assignmentExpr);
        if (TK_EOF == LOOK_AHEAD(pThis)) {
            // stop the work
            CHECK_NULLTREE_ABORT (NULL, root);
        }

        if (syncWhatCase <= 0) {
            // Error has recovered: assume current assignmentExpression
            //    is complete,
            // and Returns an incomplete tree
            goto LABEL_EXIT_RDF;
        }
    }


    // ?---?---?
    // To determine whether the current expression is an assignment,
    // we need to roll up our sleeves, and look ahead desperately
    // many many many tokens.
    //
    // This is an unavoidable issue to support arrays.
    // !---!---!


    // Guessing ...... Why?
    //  1. In an assignment expr, the first token is an ID.
    //  BUT
    //  2. If the first token is an ID, the current construct may be, or
    //     may be not an assignment expr.
    //
    bool isAssignment = false;
    if ( TK_ID == LOOK_AHEAD(pThis) )  {
        EnumTokenKind la2 = LOOK_AHEAD_K(pThis, 2);
        if (is_assignOptr_(la2) ) {
            isAssignment = true;
        } else if (TK_LBRACKET == la2) {
            // may-be Arr[id[e1]=10][e2][e3]...[var1=var2=100] = ...
            // Try a clumsy (but simple) manner ..., ^_^  LL(*),
            //     to search "=", "+=", ...
           EnumTokenKind laK = TK_LBRACKET ;
            int level = 0;
            for (int k = 2; laK != TK_EOF; laK = LOOK_AHEAD_K(pThis, ++k) ) {
                if      ( TK_LBRACKET == laK )
                    ++ level;
                else if ( TK_RBRACKET == laK )
                    --level;
                else if( 0 == level ) {
                    if ( is_assignOptr_(laK) )
                        isAssignment = true;
                    break;
                }
            }
        }
    } // end: if ( TK_ID == LOOK_AHEAD(pThis) )

    t_tree_ptr  subTree;
    if ( isAssignment ) {
        productionId = 73;
        subTree = RDF_leftValue(pThis);
        CHECK_NULLTREE_ABORT (subTree, root);
        tree_add_child(root, subTree);

        // consume the assignment operator
        subTree = parser_match(pThis, LOOK_AHEAD(pThis));
        CHECK_NULLTREE_ABORT (subTree, root);
        tree_add_child(root, subTree);

        subTree = RDF_assignmentExpression(pThis);
        CHECK_NULLTREE_ABORT (subTree, root);
        tree_add_child(root, subTree);
    }
    else
    {
        productionId = 74;
//        subTree = RDF_conditionalExpression(pThis);
        subTree = RDF_logicalOrExpression(pThis);
        CHECK_NULLTREE_ABORT (subTree, root);
        tree_add_child(root, subTree);
    }


// -------  END of local action
    if (PRD_ID_UNKNOWN == productionId) {
        ERROR_REPORT_BUG("Forgot to correct the value of "
                         "productionId in RDF_" NT_TEXT "()");
    }
    tree_reset_production( root, productionId );

LABEL_EXIT_RDF:
    exit_nonterminal  ( pThis, THIS_NT, root );
    return root;
}

